"""Health checking and monitoring for model adapters."""

import asyncio
import time
from typing import Dict, Any, List, Optional
from datetime import datetime, timedelta
import logging
from collections import defaultdict
import httpx
from openai import OpenAI
import anthropic

from .base import BaseModelAdapter


logger = logging.getLogger(__name__)


class HealthChecker:
    """Monitors health and performance of model adapters."""
    
    def __init__(self, check_interval: int = 300):
        """Initialize health checker.
        
        Args:
            check_interval: Seconds between health checks
        """
        self.check_interval = check_interval
        self._adapters: Dict[str, BaseModelAdapter] = {}
        self._health_history = defaultdict(list)
        self._alert_callbacks = []
        self._running = False
        self._task = None
    
    def register_adapter(self, name: str, adapter: BaseModelAdapter) -> None:
        """Register an adapter for health monitoring.
        
        Args:
            name: Adapter name
            adapter: Model adapter instance
        """
        self._adapters[name] = adapter
    
    def register_alert_callback(self, callback) -> None:
        """Register callback for health alerts.
        
        Args:
            callback: Async function to call on health issues
        """
        self._alert_callbacks.append(callback)
    
    async def check_health(self, adapter_name: str = None) -> Dict[str, bool]:
        """Check health of specific or all adapters.
        
        Args:
            adapter_name: Optional specific adapter to check
            
        Returns:
            Health status dictionary
        """
        results = {}
        
        if adapter_name:
            if adapter_name in self._adapters:
                adapter = self._adapters[adapter_name]
                try:
                    is_healthy = await adapter.health_check()
                    results[adapter_name] = is_healthy
                    self._record_health(adapter_name, is_healthy)
                    
                    if not is_healthy:
                        await self._trigger_alert(adapter_name, "Health check failed")
                        
                except Exception as e:
                    logger.error(f"Health check failed for {adapter_name}: {e}")
                    results[adapter_name] = False
                    self._record_health(adapter_name, False)
                    await self._trigger_alert(adapter_name, str(e))
        else:
            # Check all adapters
            tasks = {}
            for name, adapter in self._adapters.items():
                tasks[name] = adapter.health_check()
            
            responses = await asyncio.gather(*tasks.values(), return_exceptions=True)
            
            for name, response in zip(tasks.keys(), responses):
                if isinstance(response, Exception):
                    results[name] = False
                    self._record_health(name, False)
                    await self._trigger_alert(name, str(response))
                else:
                    results[name] = response
                    self._record_health(name, response)
                    if not response:
                        await self._trigger_alert(name, "Health check failed")
        
        return results
    
    def _record_health(self, adapter_name: str, is_healthy: bool) -> None:
        """Record health check result.
        
        Args:
            adapter_name: Name of adapter
            is_healthy: Health status
        """
        record = {
            "timestamp": datetime.utcnow(),
            "healthy": is_healthy
        }
        
        history = self._health_history[adapter_name]
        history.append(record)
        
        # Keep only last 100 records
        if len(history) > 100:
            self._health_history[adapter_name] = history[-100:]
    
    async def _trigger_alert(self, adapter_name: str, error: str) -> None:
        """Trigger alert callbacks.
        
        Args:
            adapter_name: Name of failed adapter
            error: Error message
        """
        for callback in self._alert_callbacks:
            try:
                await callback(adapter_name, error)
            except Exception as e:
                logger.error(f"Alert callback failed: {e}")
    
    def get_health_metrics(self, adapter_name: str = None) -> Dict[str, Any]:
        """Get health metrics for adapters.
        
        Args:
            adapter_name: Optional specific adapter
            
        Returns:
            Health metrics dictionary
        """
        metrics = {}
        
        if adapter_name and adapter_name in self._health_history:
            metrics[adapter_name] = self._calculate_metrics(adapter_name)
        else:
            for name in self._health_history:
                metrics[name] = self._calculate_metrics(name)
        
        return metrics
    
    def _calculate_metrics(self, adapter_name: str) -> Dict[str, Any]:
        """Calculate health metrics for an adapter.
        
        Args:
            adapter_name: Name of adapter
            
        Returns:
            Metrics dictionary
        """
        history = self._health_history[adapter_name]
        
        if not history:
            return {"uptime": 0, "success_rate": 0, "last_check": None}
        
        total_checks = len(history)
        successful_checks = sum(1 for h in history if h["healthy"])
        
        # Calculate uptime for last 24 hours
        cutoff = datetime.utcnow() - timedelta(hours=24)
        recent_history = [h for h in history if h["timestamp"] > cutoff]
        
        if recent_history:
            recent_successful = sum(1 for h in recent_history if h["healthy"])
            uptime_24h = (recent_successful / len(recent_history)) * 100
        else:
            uptime_24h = 0
        
        return {
            "total_checks": total_checks,
            "success_rate": (successful_checks / total_checks) * 100 if total_checks > 0 else 0,
            "uptime_24h": uptime_24h,
            "last_check": history[-1]["timestamp"] if history else None,
            "last_healthy": history[-1]["healthy"] if history else False
        }
    
    async def start_monitoring(self) -> None:
        """Start periodic health monitoring."""
        if self._running:
            return
        
        self._running = True
        self._task = asyncio.create_task(self._monitoring_loop())
        logger.info("Health monitoring started")
    
    async def stop_monitoring(self) -> None:
        """Stop periodic health monitoring."""
        self._running = False
        
        if self._task:
            self._task.cancel()
            try:
                await self._task
            except asyncio.CancelledError:
                pass
        
        logger.info("Health monitoring stopped")
    
    async def _monitoring_loop(self) -> None:
        """Main monitoring loop."""
        while self._running:
            try:
                await self.check_health()
                await asyncio.sleep(self.check_interval)
            except Exception as e:
                logger.error(f"Monitoring loop error: {e}")
                await asyncio.sleep(self.check_interval)


class ModelHealthChecker:
    """专门用于检查模型API健康状态的检查器 - 使用统一接口模式"""
    
    # 提供商配置映射 - 用于特定提供商的配置
    PROVIDER_CONFIGS = {
        # 国际服务商
        "openai": {"base_url": "https://api.openai.com/v1"},
        "anthropic": {"base_url": "https://api.anthropic.com"},  # Anthropic不需要/v1后缀
        "claude": {"base_url": "https://api.anthropic.com"},  # Claude也是Anthropic
        "deepseek": {"base_url": "https://api.deepseek.com/v1"},
        "cohere": {"base_url": "https://api.cohere.ai/v1"},
        
        # 国内服务商
        "zhipu": {"base_url": "https://open.bigmodel.cn/api/paas/v4"},
        "qianfan": {"base_url": "https://aip.baidubce.com/rpc/2.0/ai_custom/v1"},
        "tongyi": {"base_url": "https://dashscope.aliyuncs.com/compatible-mode/v1"},
        "minimax": {"base_url": "https://api.minimax.chat/v1"},
        
        # 代理服务
        "anyrouter": {"base_url": "https://anyrouter.top"},
        "custom": {}  # 用户自定义配置
    }
    
    def __init__(self):
        self.timeout = 10.0  # 默认超时时间
        
    async def check_model_health(
        self, 
        base_url: str, 
        api_key: str, 
        model_name: str,
        provider: str
    ) -> Dict[str, Any]:
        """
        使用统一的OpenAI接口模式检查所有模型健康状态
        
        返回:
            {
                "status": "available|unavailable|checking",
                "response_time": 响应时间(毫秒),
                "error": 错误信息,
                "checked_at": 检查时间
            }
        """
        start_time = time.time()
        
        try:
            # 使用统一的OpenAI接口模式测试所有提供商
            result = await self._universal_test(base_url, api_key, model_name, provider)
            
            response_time = int((time.time() - start_time) * 1000)
            result["response_time"] = response_time
            result["checked_at"] = datetime.now().isoformat()
            result["provider"] = provider
            result["model_name"] = model_name
            
            return result
            
        except Exception as e:
            logger.error(f"Health check failed for {model_name} ({provider}): {e}")
            return {
                "status": "unavailable",
                "response_time": int((time.time() - start_time) * 1000),
                "error": str(e),
                "checked_at": datetime.now().isoformat(),
                "provider": provider,
                "model_name": model_name
            }
    
    async def _universal_test(
        self,
        base_url: str,
        api_key: str,
        model_name: str,
        provider: str
    ) -> Dict[str, Any]:
        """
        统一的测试方法 - 使用OpenAI SDK测试所有兼容的API
        
        支持的提供商:
        - OpenAI及其兼容API (DeepSeek, 智谱, 通义千问等)
        - Anthropic Claude (需要特殊处理)
        - 任何OpenAI格式兼容的代理服务
        """
        try:
            # 特殊处理Anthropic/Claude
            if provider.lower() in ['anthropic', 'claude']:
                return await self._test_anthropic_model(base_url, api_key, model_name)
            
            # 创建统一的OpenAI客户端
            client = OpenAI(
                api_key=api_key,
                base_url=base_url,
                timeout=self.timeout
            )
            
            # 统一的测试消息
            test_message = "Hi, this is a connection test."
            
            # 发送测试请求
            logger.info(f"Testing {provider} model {model_name} at {base_url}")
            response = client.chat.completions.create(
                model=model_name,
                messages=[{"role": "user", "content": test_message}],
                max_tokens=1,  # 最小token数，只测试连通性
                temperature=0
            )
            
            # 解析响应 - 兼容多种格式
            if self._validate_response(response):
                logger.info(f"✅ {provider} model {model_name} test successful")
                return {"status": "available", "message": "连接测试成功"}
            else:
                logger.warning(f"⚠️ {provider} model {model_name} returned unexpected response format")
                return {"status": "available", "message": "连接成功但响应格式异常"}
                
        except Exception as e:
            return self._handle_test_error(e, provider, model_name)
    
    async def _test_anthropic_model(self, base_url: str, api_key: str, model_name: str) -> Dict[str, Any]:
        """专门测试Anthropic Claude模型"""
        try:
            # 处理base_url格式 - 只影响Anthropic/Claude模型
            processed_base_url = base_url
            if base_url:
                processed_base_url = base_url.rstrip('/')
                # 如果base_url已经以/v1结尾，移除它让SDK重新添加
                # 例如：http://154.12.94.140:3000/api/v1 -> http://154.12.94.140:3000/api
                if processed_base_url.endswith('/v1'):
                    processed_base_url = processed_base_url[:-3]

            # 创建异步Anthropic客户端，并添加Claude Code客户端标识
            default_headers = {
                "anthropic-client": "claude-code",
                "user-agent": "claude-code/1.0"
            }

            if not base_url or 'api.anthropic.com' in base_url:
                # 使用官方API
                client = anthropic.AsyncAnthropic(
                    api_key=api_key,
                    default_headers=default_headers
                )
            else:
                # 使用自定义base_url（如中继服务）
                # processed_base_url应该是 http://154.12.94.140:3000/api
                # SDK会自动添加/v1变成 http://154.12.94.140:3000/api/v1
                client = anthropic.AsyncAnthropic(
                    api_key=api_key,
                    base_url=processed_base_url,
                    default_headers=default_headers
                )

            logger.info(f"Testing Anthropic model {model_name} at {base_url or 'official API'}")

            # 发送测试请求
            response = await client.messages.create(
                model=model_name,
                messages=[{"role": "user", "content": "Hi"}],
                max_tokens=5
            )

            if response.content:
                logger.info(f"✅ Anthropic model {model_name} test successful")
                return {"status": "available", "message": "Anthropic模型连接测试成功"}
            else:
                return {"status": "unavailable", "error": "Anthropic API响应为空"}

        except Exception as e:
            logger.error(f"❌ Anthropic model {model_name} test failed: {e}")
            return self._handle_test_error(e, "anthropic", model_name)
    
    def _validate_response(self, response) -> bool:
        """验证响应格式是否正确"""
        # 处理字符串响应（某些代理服务）
        if isinstance(response, str):
            return "error" not in response.lower()
        
        # 处理标准OpenAI响应对象
        if hasattr(response, 'choices'):
            return bool(response.choices and len(response.choices) > 0)
        
        # 处理字典响应
        if isinstance(response, dict):
            return 'choices' in response or 'content' in response
        
        # 其他格式默认认为成功（宽松模式）
        return True
    
    def _handle_test_error(self, error: Exception, provider: str, model_name: str) -> Dict[str, Any]:
        """统一的错误处理"""
        error_msg = str(error).lower()
        error_str = str(error)
        logger.error(f"❌ {provider} model {model_name} test failed: {error}")
        
        # 错误分类和中文提示
        if "403" in error_msg or "forbidden" in error_msg:
            return {"status": "unavailable", "error": "访问被拒绝：请检查API密钥权限或服务是否启用"}
        elif "401" in error_msg or "authentication" in error_msg or "unauthorized" in error_msg or "invalid.*key" in error_msg:
            return {"status": "unavailable", "error": "认证失败：API密钥无效或已过期"}
        elif "not found" in error_msg or "404" in error_msg:
            return {"status": "unavailable", "error": f"模型 {model_name} 不存在或路径错误"}
        elif "rate limit" in error_msg or "429" in error_msg:
            return {"status": "unavailable", "error": "请求频率超限，请稍后重试"}
        elif "timeout" in error_msg:
            return {"status": "unavailable", "error": "连接超时，请检查网络或增加超时时间"}
        elif "connection" in error_msg or "connect" in error_msg:
            return {"status": "unavailable", "error": "无法连接到服务器，请检查URL和网络"}
        elif "quota" in error_msg or "insufficient" in error_msg:
            return {"status": "unavailable", "error": "配额不足，请检查账户余额"}
        elif "invalid.*model" in error_msg or "model.*not.*found" in error_msg:
            return {"status": "unavailable", "error": f"不支持的模型：{model_name}"}
        else:
            # 返回原始错误信息，但限制长度并格式化
            if len(error_str) > 200:
                return {"status": "unavailable", "error": f"API错误：{error_str[:200]}..."}
            else:
                return {"status": "unavailable", "error": f"API错误：{error_str}"}
    
    async def _check_openai_compatible(
        self, 
        base_url: str, 
        api_key: str, 
        model_name: str
    ) -> Dict[str, Any]:
        """检查OpenAI兼容API的模型 - 向后兼容方法"""
        return await self._universal_test(base_url, api_key, model_name, "openai")
    
    async def _check_anthropic(
        self, 
        base_url: str, 
        api_key: str, 
        model_name: str
    ) -> Dict[str, Any]:
        """检查Anthropic Claude模型 - 向后兼容方法"""
        return await self._universal_test(base_url, api_key, model_name, "anthropic")
    
    async def batch_check_health(
        self, 
        models: list
    ) -> Dict[str, Dict[str, Any]]:
        """批量检查多个模型的健康状态"""
        tasks = []
        model_ids = []
        
        for model in models:
            task = self.check_model_health(
                model["base_url"],
                model["api_key"],
                model["model_name"],
                model["provider"]
            )
            tasks.append(task)
            model_ids.append(model.get("id", model["model_name"]))
        
        results = await asyncio.gather(*tasks, return_exceptions=True)
        
        health_results = {}
        for model_id, result in zip(model_ids, results):
            if isinstance(result, Exception):
                health_results[model_id] = {
                    "status": "unavailable",
                    "error": str(result),
                    "checked_at": datetime.now().isoformat()
                }
            else:
                health_results[model_id] = result
        
        return health_results